#include "htf_zlib_wrapper.h"
#include <zlib.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>

int htf_zlib_decompress(const uint8_t* in, size_t in_size,
                        uint8_t** out, size_t* out_size) {
    if (!in || !out || !out_size || in_size == 0) {
        return -1;
    }

    z_stream strm = {0};
    strm.next_in = (Bytef*)in;
    strm.avail_in = (uInt)in_size;

    // Check whether in_size exceeds the uInt range of zlib (typically 32-bit)
    if (in_size > (size_t)UINT32_MAX) {
        return -1;
    }

    int ret = inflateInit(&strm);
    if (ret != Z_OK) {
        return -1;
    }

    // Initial allocation: at least 1 KB or twice in_size
    size_t buf_size = (in_size < 1024) ? 1024 : in_size * 2;
    uint8_t* buf = (uint8_t*)malloc(buf_size);
    if (!buf) {
        inflateEnd(&strm);
        return -1;
    }

    strm.next_out = (Bytef*)buf;
    strm.avail_out = (uInt)buf_size;

    while (1) {
        ret = inflate(&strm, Z_NO_FLUSH);

        if (ret == Z_STREAM_END) {
            break;
        }

        if (ret != Z_OK) {
            free(buf);
            inflateEnd(&strm);
            return -2;
        }

        // More space needed: expand the buffer
        size_t old_size = buf_size;
        buf_size *= 2;
        uint8_t* new_buf = (uint8_t*)realloc(buf, buf_size);
        if (!new_buf) {
            free(buf);
            inflateEnd(&strm);
            return -1;
        }
        buf = new_buf;

        // Update strm to point to the end of the new buffer
        strm.next_out = (Bytef*)buf + old_size;
        strm.avail_out = (uInt)(buf_size - old_size);
    }

    *out = buf;
    *out_size = (size_t)strm.total_out;
    inflateEnd(&strm);
    return 0;
}